package com.zhuoli.service.springboot.mybatis.pagehelper.common;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Author: zhuoli
 * @Date: 2018/8/19 16:08
 * @Description:
 */
@Slf4j
public class ExampleFactory {

    private List<FieldInfo> fieldInfoList;
    private String orderBy;

    private ExampleFactory(List<FieldInfo> fieldInfoList, String orderBy) {
        this.fieldInfoList = fieldInfoList;
        this.orderBy = orderBy;
    }

    public <T> T getExample(Class<T> exampleClazz) {
        T example = null;
        try {
            example = exampleClazz.newInstance();
        } catch (Exception e) {
            log.info("exampleClazz is illegal, message is {}", e);
        }
        try {
            Class criteriaClazz = Class.forName(exampleClazz.getName() + "$Criteria");
            Method createCriteria = exampleClazz.getDeclaredMethod("createCriteria");
            Object criteria = createCriteria.invoke(example);
            for (FieldInfo fieldInfo : fieldInfoList) {
                if (!fieldInfo.getRelation().contains("Null") && Objects.isNull(fieldInfo.getFieldValue())) {
                    continue;
                }
                String methodName = "and" + fieldInfo.getFieldName() + fieldInfo.getRelation();

                if (fieldInfo.getRelation().contains("Null")) {
                    Method method = criteriaClazz.getDeclaredMethod(methodName);
                    method.invoke(criteria);
                } else if (fieldInfo.getRelation().contains("Between")) {
                    Object begin = ((List) fieldInfo.getFieldValue()).get(0);
                    Object end = ((List) fieldInfo.getFieldValue()).get(1);
                    Method method = criteriaClazz.getDeclaredMethod(methodName, begin.getClass(), end.getClass());
                    method.invoke(criteria, begin , end);
                } else {
                    Object parameterValue = fieldInfo.getFieldValue();
                    if (parameterValue instanceof String && fieldInfo.getRelation().equals("Like")) {
                        parameterValue = "%" + parameterValue + "%";
                    }
                    Method method = criteriaClazz.getDeclaredMethod(methodName, fieldInfo.getFieldValue().getClass());
                    method.invoke(criteria, parameterValue);
                }
            }
            if (Objects.nonNull(orderBy)) {
                Method setOrderByClauseMethod = exampleClazz.getDeclaredMethod("setOrderByClause", String.class);
                setOrderByClauseMethod.invoke(example, orderBy);
            }

        } catch (Exception e) {
            log.info("Criteria create failed, message is {}", e);
        }

        return example;
    }

    public static class ExampleFactoryBuilder {
        private List<FieldInfo> fieldInfoList = new ArrayList<>();
        private String orderBy;

        public ExampleFactoryBuilder add(FieldInfo fieldInfo) {
            fieldInfoList.add(fieldInfo);
            return this;
        }

        public ExampleFactoryBuilder withDeleted(Boolean withDeleted) {
            if (withDeleted == null || !withDeleted) {
                fieldInfoList.add(new FieldInfo("IsDeleted", 0, RelationEnum.EQUAL_TO.getValue()));
            }
            return this;
        }

        public ExampleFactoryBuilder setOrderByClause(String orderBy) {
            if (orderBy != null) {
                this.orderBy = orderBy;
            }
            return this;
        }

        public ExampleFactory build() {
            return new ExampleFactory(fieldInfoList, orderBy);
        }
    }

    public static ExampleFactoryBuilder builder() {
        return new ExampleFactoryBuilder();
    }
}